use std::{rc::Rc, cell::RefCell};

use crate::{native::{self, NativeMethod, allocate, reallocate, free, memory_at, memory_copy}, rtda::{Frame, heap::string_pool, OperandStack}};

pub fn init() {
    _unsafe(allocate_memory, "allocateMemory", "(J)J");
	_unsafe(reallocate_memory, "reallocateMemory", "(JJ)J");
	_unsafe(free_memory, "freeMemory", "(J)V");
	// _unsafe(addressSize, "addressSize", "()I");
	//	_unsafe(putAddress, "putAddress", "(JJ)V");
	//	_unsafe(getAddress, "getAddress", "(J)J");
	//	_unsafe(mem_putByte, "putByte", "(JB)V");
	_unsafe(mem_get_byte, "getByte", "(J)B");
	//	_unsafe(mem_putShort, "putShort", "(JS)V");
	//	_unsafe(mem_getShort, "getShort", "(J)S");
	//	_unsafe(mem_putChar, "putChar", "(JC)V");
	//	_unsafe(mem_getChar, "getChar", "(J)C");
	//	_unsafe(mem_putInt, "putInt", "(JI)V");
	//	_unsafe(mem_getInt, "getInt", "(J)I");
	_unsafe(mem_put_long, "putLong", "(JJ)V");
	//	_unsafe(mem_getLong, "getLong", "(J)J");
	//	_unsafe(mem_putFloat, "putFloat", "(JF)V");
	//	_unsafe(mem_getFloat, "getFloat", "(J)F");
	//	_unsafe(mem_putDouble, "putDouble", "(JD)V");
	//	_unsafe(mem_getDouble, "getDouble", "(J)D");
}


fn _unsafe(method: NativeMethod, name: &str, desc: &str) {
	native::register("sun/misc/Unsafe", name, desc, method)
}

// public native long allocateMemory(long bytes);
// (J)J
fn allocate_memory(frame: Rc<RefCell<Frame>>) {
	let vars = frame.borrow().get_local_vars();
	// vars.GetRef(0) // this
    let bytes = vars.borrow().get_long(1);

    let address = allocate(bytes);
	let stack = frame.borrow().get_operand_stack();
	stack.borrow_mut().push_long(address);
}

// public native long reallocateMemory(long address, long bytes);
// (JJ)J
fn reallocate_memory(frame: Rc<RefCell<Frame>>) {
	let vars = frame.borrow().get_local_vars();
	// vars.GetRef(0) // this
    let address = vars.borrow().get_long(1);
    let bytes = vars.borrow().get_long(3);

    let new_address = reallocate(address, bytes);
	let stack = frame.borrow().get_operand_stack();
	stack.borrow_mut().push_long(new_address);
}

// public native void freeMemory(long address);
// (J)V
fn free_memory(frame: Rc<RefCell<Frame>>) {
	let vars = frame.borrow().get_local_vars();
	// vars.GetRef(0) // this
    let address = vars.borrow().get_long(1);
    free(address);
}

// public native byte getByte(long address);
// (J)B
fn mem_get_byte(frame: Rc<RefCell<Frame>>) {
	let (stack, mem) = _get(frame);
    // stack.PushInt(int32(Int8(mem)))
	stack.borrow_mut().push_int(Int8(mem) as i32);
}

// public native void putLong(long address, long x);
// (JJ)V
fn mem_put_long(frame: Rc<RefCell<Frame>>) {
	let vars = frame.borrow().get_local_vars();
	// vars.GetRef(0) // this
    let address = vars.borrow().get_long(1);
    let value = vars.borrow().get_long(3);

	memory_copy(address, &value.to_be_bytes());
}

fn _get(frame: Rc<RefCell<Frame>>) -> (Rc<RefCell<OperandStack>>, Vec<u8>) {
    let vars = frame.borrow().get_local_vars();
    // vars.GetRef(0) // this
    let address = vars.borrow().get_long(1);
    let stack = frame.borrow().get_operand_stack();
    let mem = memory_at(address);
    (stack, mem)
}

fn Int8(s: Vec<u8>) -> i8 {
    s[0] as i8
}